/* stat() */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/* getopt_long() */
#include <getopt.h>

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

#include "mtd.h"
#include "mtd_sim.h"
#include "yaffs_guts.h"
#include "yaffs_read.h"

#include "file_list_tree.h"
#include "file_recover.h"

struct mtd_info mtd;

char *yaffs_obj_type_str[] = {"unknown", "file", "symlink", "directory", "hardlink", "special", NULL};

void print_yaffs_obj_hdr(struct yaffs_obj_hdr *hdr);
int debug_iterate_chunks(struct mtd_info *mtd);
void interactive_mode(FILE *, FILE *);

struct file_list_tree_t *ptree;

void usage(void)
{
	fprintf(stderr, 
			"yaffs2recover [options] NAND-image\n"
			"Options: \n"
			"	-h	--help	show this help message and quit\n"
			"	-n	--dry-run	do not run recovery (print info only)\n"
			"	-m	--mtd-info	print NAND image info\n"
			"	-c	--chunks-info	iterate all YAFFS2 chunks and print info\n"
			"	-i	interactive mode; long argument needs a pipe file\n"
			"	--fifo-in	interactive mode; specify an input pipe\n"
			"	--fifo-out	interactive mode; specify an output pipe\n"
			);
}

int 
main(int argc, char **argv)
{

	int r, c, 
		option_index,
		flag_n = 0,
		flag_m = 0,
		flag_c = 0, 
		flag_i = 0;
	struct stat stat_fifo;

	FILE *pipe_in = NULL, *pipe_out = NULL;

	static struct option long_options[] =
	{
		{"help",     no_argument,       0, 'h'},
		{"dry-run",  no_argument,       0, 'n'},
		{"mtd-info",  no_argument, 0, 'm'},
		{"chunks-info",  no_argument, 0, 'c'},
		{"interactive", no_argument, 0, 'i'},
		{"fifo-in",  required_argument, 0, 'f'},
		{"fifo-out", required_argument, 0, 'o'},
		{0, 0, 0, 0}
	};
	
	if (argc < 2) {
		goto usage;
	}

	while (1) {
		c = getopt_long(argc, argv, "hnmci", long_options, &option_index);
		if (c == -1)
			break;
		switch (c) {
			case 'h':
				goto usage;
			
			case 'n':
				flag_n = 1;
				break;
			case 'm':
				flag_m = 1;
				break;
			case 'c':
				flag_c = 1;
				break;
			case 'i':
				flag_i = 1;
				break;

			case 'f':
				if (stat(optarg, &stat_fifo) || 
						S_ISFIFO(stat_fifo.st_mode) ||
						(pipe_in = fopen(optarg,"r")) == NULL) {
					fprintf(stderr, "Invalid fifo-in FIFO\n");
					goto error;
				}
				flag_i = 1;
				break;

			case 'o':
				if (stat(optarg, &stat_fifo) || 
						S_ISFIFO(stat_fifo.st_mode) ||
						(pipe_out = fopen(optarg,"w")) == NULL) {
					fprintf(stderr, "Invalid fifo-out FIFO\n");
					goto error;
				}
				flag_i = 1;
				break;
		}
			
	}


	if (optind >= argc)
		goto usage;
	
	r = mtd_sim_init(&mtd, argv[optind]);
	if (r) {
		printf("Error: (%d)\n", r);
		goto error;
	}
	/* shadow all other options */
	if (flag_i) {
		ptree = build_file_list_tree(&mtd);
		interactive_mode(pipe_in, pipe_out);
		exit(0);
	}

	if (flag_m)
		debug_print_mtd_info(&mtd);

	ptree = build_file_list_tree(&mtd);
	if (!ptree)
		goto error;

	if (flag_c)
		debug_iterate_chunks(&mtd);

	if (!flag_n)
		file_recover_iterate_tree(&mtd, ptree);

	exit(0);

error:
	exit(-1);

usage:
	usage();
	exit(0);
}

void
debug_print_mtd_info(struct mtd_info *mtd)
{
	printf("mtd_info:\n"
			"	size = %d\n"
			"	erasesize = %d\n"
			"	writesize = %d\n"
			"	chunks_per_block = %d\n"
			"	nblocks = %d\n"
			, mtd->size, mtd->erasesize, mtd->writesize, mtd->erasesize / mtd->writesize, mtd->size / mtd->erasesize);
}

int
debug_iterate_chunks(struct mtd_info *mtd)
{
	int i,r, n_usedchunks = 0, n_hdrchunks = 0;
	void *data;
	struct yaffs_ext_tags tags;
	

	data = malloc(totalBytesPerChunk);
	printf("totalChunks = %d\n", totalChunks);

	for (i = 0; i < totalChunks; i++) {
		memset(data, 0, totalBytesPerChunk);
		if ( (r = yaffs_read_chunk_with_tags(mtd, i, data, &tags)) != YAFFS_OK)
			goto error;
		/* object header */
		if (tags.chunk_used)
			n_usedchunks++;
		if (tags.chunk_used && tags.chunk_id == 0 && tags.obj_id != 0) {
			n_hdrchunks++;
			printf("Object header found in chunk %d: \n", i);
			print_yaffs_obj_hdr((struct yaffs_obj_hdr *) data);
		}
	}
	printf("Total used chunks: %d. Total header chunks: %d\n", n_usedchunks, n_hdrchunks);
	return 0;

error:
	return r;
	
}

void
print_yaffs_obj_hdr(struct yaffs_obj_hdr *hdr)
{
	printf("yaffs_obj_hdr info:\n"
			"	Object name: %s\n"
			"	Object type %s (%d)\n", hdr->name, yaffs_obj_type_str[hdr->type], hdr->file_size);

}
